<template>
  <div ref="container"></div>
</template>

<script>
import * as THREE from 'three'
import { onMounted, onUnmounted, ref } from 'vue'

export default {
  props: {
    targetId: {
      type: String,
      default: ''
    },
    // 雨滴强度
    intensity: {
      type: Number,
      default: 0.4
    },
    // 雨滴下落速度
    speed: {
      type: Number,
      default: 0.9
    },
    // 整体亮度
    brightness: {
      type: Number,
      default: 0.8
    },
    // 法线贴图强度
    normal: {
      type: Number,
      default: 0.5
    },
    // 缩放系数
    zoom: {
      type: Number,
      default: 2.61
    },
    // 模糊强度
    blurIntensity: {
      type: Number,
      default: 0.5
    },
    // 模糊迭代次数
    blurIterations: {
      type: Number,
      default: 16
    },
    // 是否启用画面平移
    panning: {
      type: Boolean,
      default: false
    },
    // 是否启用后期处理
    postProcessing: {
      type: Boolean,
      default: true
    },
    // 是否启用闪电效果
    lightning: {
      type: Boolean,
      default: false
    },
    // 是否启用纹理填充
    textureFill: {
      type: Boolean,
      default: true
    }
  },
  setup(props) {
    const container = ref(null)
    let scene, camera, renderer, material, clock, gui
    let isPaused = false,
      elapsedResetTime = 21600,
      elapsedPreviousTime = 0
    let devicePixelRatio = window.devicePixelRatio || 1
    
    let settings = { fps: 30, scale: 1.0, parallaxVal: 1 }
    let videoElement

    const init = async () => {
      renderer = new THREE.WebGLRenderer({
        antialias: false,
      })
      const targetElement = props.targetId ? document.getElementById(props.targetId) : container.value
      const { width, height } = targetElement.getBoundingClientRect()
      renderer.setSize(width, height)
      renderer.setPixelRatio(settings.scale * devicePixelRatio)
      targetElement.appendChild(renderer.domElement)
      scene = new THREE.Scene()
      camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1)
      clock = new THREE.Clock()
      clock.start()

      material = new THREE.ShaderMaterial({
        uniforms: {
          // 纹理采样器
          u_tex0: { type: 't' },
          // 时间变量,用于动画
          u_time: { value: 0, type: 'f' },
          // 雨滴强度
          u_intensity: { value: props.intensity, type: 'f' },
          // 雨滴下落速度
          u_speed: { value: props.speed, type: 'f' },
          // 整体亮度
          u_brightness: { value: props.brightness, type: 'f' },
          // 法线贴图强度
          u_normal: { value: props.normal, type: 'f' },
          // 缩放系数
          u_zoom: { value: props.zoom, type: 'f' },
          // 模糊强度
          u_blur_intensity: { value: props.blurIntensity, type: 'f' },
          // 模糊迭代次数
          u_blur_iterations: { value: props.blurIterations, type: 'i' },
          // 是否启用画面平移
          u_panning: { value: props.panning, type: 'b' },
          // 是否启用后期处理
          u_post_processing: { value: props.postProcessing, type: 'b' },
          // 是否启用闪电效果
          u_lightning: { value: props.lightning, type: 'b' },
          // 是否启用纹理填充
          u_texture_fill: { value: props.textureFill, type: 'b' },
          // 当前渲染分辨率
          u_resolution: { 
            value: new THREE.Vector2(window.innerWidth, window.innerHeight), 
            type: 'v2' 
          },
          // 纹理原始分辨率
          u_tex0_resolution: { 
            value: new THREE.Vector2(window.innerWidth, window.innerHeight), 
            type: 'v2' 
          },
        },
        vertexShader: `
          varying vec2 vUv;        
          void main() {
              vUv = uv;
              gl_Position = vec4( position, 1.0 );    
          }
        `,
      })
      
      material.fragmentShader = await (await fetch("/shaders/rain.frag")).text()
      resize()

      material.uniforms.u_tex0_resolution.value = new THREE.Vector2(1920, 1080)
      material.uniforms.u_tex0.value = await new THREE.TextureLoader().loadAsync("/media/image.webp")

      const quad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2, 1, 1), material)
      scene.add(quad)

      window.addEventListener('resize', resize)
      render()
      datUI()
    }

    const setScale = (userScale) => {
      settings.scale = userScale
      const finalScale = devicePixelRatio * settings.scale
      if (renderer.getPixelRatio() == finalScale) return

      renderer.setPixelRatio(finalScale)
      material.uniforms.u_resolution.value = new THREE.Vector2(
        window.innerWidth * finalScale,
        window.innerHeight * finalScale
      )
    }

    const resize = () => {
      if (window.devicePixelRatio !== devicePixelRatio) {
        devicePixelRatio = window.devicePixelRatio || 1
        setScale(settings.scale)
      }
      const finalScale = devicePixelRatio * settings.scale

      const targetElement = props.targetId ? document.getElementById(props.targetId) : container.value
      const { width, height } = targetElement.getBoundingClientRect()
      renderer.setSize(width, height)
      material.uniforms.u_resolution.value = new THREE.Vector2(
        width * finalScale,
        height * finalScale
      )
    }

    const render = () => {
      setTimeout(function () {
        requestAnimationFrame(render)
      }, 1000 / settings.fps)

      //reset every 6hr
      if (clock.getElapsedTime() > elapsedResetTime) clock = new THREE.Clock()
      material.uniforms.u_time.value = clock.getElapsedTime()

      // 更新材质参数
      material.uniforms.u_intensity.value = props.intensity
      material.uniforms.u_speed.value = props.speed
      material.uniforms.u_brightness.value = props.brightness
      material.uniforms.u_normal.value = props.normal
      material.uniforms.u_zoom.value = props.zoom
      material.uniforms.u_blur_intensity.value = props.blurIntensity
      material.uniforms.u_blur_iterations.value = props.blurIterations
      material.uniforms.u_panning.value = props.panning
      material.uniforms.u_post_processing.value = props.postProcessing
      material.uniforms.u_lightning.value = props.lightning
      material.uniforms.u_texture_fill.value = props.textureFill

      renderer.render(scene, camera)
    }

    const datUI = () => {
      // GUI setup code here
    }

    const createVideoElement = (src) => {
      let htmlVideo = document.createElement("video")
      htmlVideo.src = src
      htmlVideo.muted = true
      htmlVideo.loop = true
      htmlVideo.play()
      return htmlVideo
    }

    const disposeVideoElement = (video) => {
      if (video) {
        video.pause()
        video.removeAttribute('src')
        video.load()
      }
    }

    onMounted(() => {
      init()
    })

    onUnmounted(() => {
      window.removeEventListener('resize', resize)
      scene.remove.apply(scene, scene.children)
      renderer.dispose()
      material.dispose()
      if (gui) gui.destroy()
    })

    return {
      container
    }
  }
}
</script>